package com.softserveinc.moviedbapp.fragment;

import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.ActionBar;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.SupportActivity;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.ListView;

import com.softserveinc.moviedbapp.R;
import com.softserveinc.moviedbapp.adapter.MovieAdapter;
import com.softserveinc.moviedbapp.application.MovieDBApplication;
import com.softserveinc.moviedbapp.manager.DataManager;
import com.softserveinc.moviedbapp.manager.DataManager.AsyncTaskWrapper;
import com.softserveinc.moviedbapp.model.Movie;
import com.softserveinc.moviedbapp.provider.MovieListType;

import java.util.ArrayList;
import java.util.List;

public class MovieListFragment extends Fragment implements ListView.OnScrollListener {

	private static final boolean DEBUG = true;
	private static final int DEFAULT_ROWS = 5;
	private static final int DEFAULT_VISIBLE_ROWS = 3;
	private static final int FIRST_IN_A_PAIR = 0;
	private static final int SECOND_IN_A_PAIR = 1;

	private MovieListType fragmentType;
	private MovieAdapter movieAdapter;
	private ActionBar actiontionBar;
	private View progressBar;
	private String searchQuery;

	private boolean adapterEmpty = true;
	private boolean startupList;
	private boolean wentFetching;

	private int adapterChildCount;
	private int firstVisPosScrollStart;
	private int lastVisPosScrollStart;

	private OnMovieSelectedListener onMovieSelectedListener;

	private DataManager dataMananger;
	private ListView listView;

	private String TAG;
	private View view;

	private AsyncTaskWrapper getListTask;
	private AsyncTaskWrapper getPostersTask;

	public MovieListFragment(final MovieListType type) {
		fragmentType = type;
		TAG = MovieListFragment.class.getSimpleName() + " " + fragmentType;
		if (DEBUG) {
			Log.i(TAG, "constructor MovieListFragment()");
		}
	}

	/* a callback interface that notifies movie info fragment about the event in this fragment */
	public interface OnMovieSelectedListener {
		public void onMovieSelected(Movie movie);
	}

	@Override
	public void onCreate(final Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		if (DEBUG) {
			Log.d(TAG, "onCreate()");
		}
		setRetainInstance(true);
	}

	@Override
	public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
		if (DEBUG) {
			Log.d(TAG, "onCreateView()");
		}
		view = inflater.inflate(R.layout.movie_list_view, null);
		listView = (ListView) view.findViewById(R.id.movie_list);
		return view;
	}

	@Override
	public void onViewCreated(final View view, final Bundle savedInstanceState) {
		super.onViewCreated(view, savedInstanceState);
		listView.setSelector(android.R.color.transparent);
	}

	@Override
	public void onAttach(final SupportActivity activity) {
		super.onAttach(activity);
		/* we want this page to display when orientation changes */
		onMovieSelectedListener = (OnMovieSelectedListener) activity;
	}

	@Override
	public void onActivityCreated(final Bundle savedInstanceState) {
		super.onActivityCreated(savedInstanceState);
		if (DEBUG) {
			Log.d(TAG, "inside onActivityCreated()");
		}
		if (savedInstanceState != null && !savedInstanceState.isEmpty()) {
			searchQuery = savedInstanceState.getString("search_query");
			Log.i(TAG, "onActivityCreated()  search query " + searchQuery);
		}
		/* get the reference to the action bar from the main fragment */
		actiontionBar = ((FragmentActivity) getActivity()).getSupportActionBar();
		progressBar = actiontionBar.getCustomView().findViewById(R.id.progressBar);

		/* add a loading progress bar as a footer to the list view */
		if (fragmentType != MovieListType.SEARCH) {
			View listViewFooter = getLayoutInflater(savedInstanceState).inflate(R.layout.loading_footer, null);
			listView.addFooterView(listViewFooter, null, false);
		}

		/* set-up the movie adapter */
		if (movieAdapter == null) {
			movieAdapter = new MovieAdapter(getActivity(), R.layout.grid_item_row, new ArrayList<Movie[]>());
		}
		movieAdapter.setOnMovieSelectedListener(onMovieSelectedListener);
		listView.setAdapter(movieAdapter);

		listView.setOnScrollListener(this);

		MovieDBApplication app = (MovieDBApplication) getActivity().getApplication();
		dataMananger = app.getDataManager();
		dataMananger.getMovieListFragmentMap().put(fragmentType, new IMovieListFragmentCallback() {

			@Override
			/* show progress text and progress icon on the action bar */
			public void onGettingMovieList() {
				progressBar.setVisibility(View.VISIBLE);
			}

			@Override
			/* add movies to the Adapter */
			public void onMovieListRetrieved(final List<Movie> movies) {
				if (DEBUG) {
					Log.i(TAG, "inside onMovieListRetrieved(final List<Movie> movies)");
					Log.i(TAG, movies.toString());
				}
				movieAdapter.clear();
				addMoviePairsToAdapter(movies, movieAdapter);
				/* add all the movies + the footer */
				adapterEmpty = false;
				adapterChildCount = movieAdapter.getCount();
				movieAdapter.notifyDataSetChanged();
				if (DEBUG) {
					Log.e(TAG, "mMovieAdapter.getCount() after resetting movies = " + adapterChildCount);
				}
				/* now download posters */
				getPostersForVisibleMovies();
				progressBar.setVisibility(View.INVISIBLE);
			}

			@Override
			public void onMovieListUpdated(final List<Movie> movies) {
				if (DEBUG) {
					Log.i(TAG, "inside onMovieListUpdated(final List<Movie> movies)");
					Log.i(TAG, movies.toString());
				}
				wentFetching = false;
				addMoviePairsToAdapter(movies, movieAdapter);
				/* add all the movies + the footer */
				movieAdapter.notifyDataSetChanged();
				adapterChildCount = movieAdapter.getCount();

				/* now download posters if it's the first pass */
				getPostersForVisibleMovies();

				progressBar.setVisibility(View.INVISIBLE);
				if (DEBUG) {
					Log.e(TAG, "mMovieAdapter.getCount() after adding movies = " + adapterChildCount);
				}
			}

			@Override
			public void onMoviePosterRetreived() {
				if (DEBUG) {
					Log.i(TAG, "inside onMoviePosterRetreived()");
				}
				movieAdapter.notifyDataSetChanged();
			}

			@Override
			public void onMovieListRetrievalFailed() {
				showFailureDialog();
			}
		});
		/* data manager is set up, so you can call it from MovieListHostFragment */
		if (startupList) {
			/* get movies from cache or download them if needed */
			if (DEBUG) {
				Log.e(TAG, "going to get movies for the startup page");
			}
			getListTask = dataMananger.provideMovieList(fragmentType, searchQuery);
		}
	}

	@Override
	public void onResume() {
		super.onResume();
		if (DEBUG) {
			Log.i(TAG, "onResume() and fragmentType is " + fragmentType);
		}
	}

	@Override
	public void onSaveInstanceState(final Bundle outState) {
		if (searchQuery != null) {
			Log.d(TAG, "in onSaveInstanceState() mSearchQuery = " + searchQuery + " fragmentType = " + fragmentType);
			outState.putString("search_query", searchQuery);
		}
	}

	@Override
	public void onPause() {
		if (getListTask != null) {
			getListTask.cancel();
		}
		cancelPosterFetching();

		super.onPause();
		Log.d(TAG, "onPause() for fragmentType " + fragmentType);
	}

	@Override
	public void onDestroyView() {
		super.onDestroyView();
		if (DEBUG) {
			Log.i(TAG, "onDestroyView() and fragmentType is " + fragmentType);
		}
	}

	public void setSearchQuery(final String mSearchQuery) {
		this.searchQuery = mSearchQuery;
	}

	public boolean isAdapterEmpty() {
		return adapterEmpty;
	}

	public void setStartupList(final boolean startupList) {
		this.startupList = startupList;
	}

	public DataManager getDataMananger() {
		return dataMananger;
	}

	@Override
	public void onScroll(final AbsListView view, final int firstVisibleItem, final int visibleItemCount,
			final int totalItemCount) {
		// TODO Auto-generated method stub
	}

	@Override
	public void onScrollStateChanged(final AbsListView view, final int scrollState) {
		if (DEBUG) {
			Log.d(TAG, "onScrollStateChanged");
		}
		switch (scrollState) {
			case SCROLL_STATE_IDLE:
				Log.e(TAG, "SCROLL_STATE_IDLE");
				getPostersForVisibleMovies();
				movieAdapter.setBusy(false);
				movieAdapter.notifyDataSetChanged();
				if (isEndOfList()) {
					if (DEBUG) {
						Log.e(TAG, "going to get movies for the next page");
					}
					getListTask = dataMananger.provideNextPage(fragmentType);
				}
				/* recycle posters that are not visible */
				int newFirst = view.getFirstVisiblePosition();
				int newLast = view.getLastVisiblePosition();
				if (DEBUG) {
					Log.d(TAG, "vis p positions new " + newFirst + " " + newLast);
				}
				boolean needRecycle = needRecycle(newFirst, newLast);
				if (needRecycle) {
					movieAdapter.recycleLastVisiblePosters(firstVisPosScrollStart, lastVisPosScrollStart);
				}
				firstVisPosScrollStart = newFirst;
				lastVisPosScrollStart = newLast;

				break;
			case SCROLL_STATE_TOUCH_SCROLL:
				Log.d(TAG, "SCROLL_STATE_TOUCH_SCROLL");
				/* if there's already a task running to get posters - cancel */
				cancelPosterFetching();
				/* get positions for visible rows if it's the first time */
				if (lastVisPosScrollStart == 0) {
					firstVisPosScrollStart = view.getFirstVisiblePosition();
					lastVisPosScrollStart = view.getLastVisiblePosition();
				}
				if (DEBUG) {
					Log.d(TAG, "vis p positions current " + firstVisPosScrollStart + " " + lastVisPosScrollStart);
				}
				movieAdapter.setBusy(true);
				break;
			case SCROLL_STATE_FLING:
				Log.d(TAG, "SCROLL_STATE_FLING");
				/* if there's already a task running to get posters - cancel */
				cancelPosterFetching();
				movieAdapter.setBusy(true);
				break;
			default:
				Log.d(TAG, "-=UNKNOWN=-");
				break;
		}
	}

	public void notifyAdapter(final boolean go) {
		if (DEBUG) {
			Log.d(TAG, "notifyAdapter  go? " + go + " fragment " + fragmentType);
		}
		if (go) {
			getPostersForVisibleMovies();
		}
		movieAdapter.setBusy(!go);
		movieAdapter.notifyDataSetChanged();
	}

	private void cancelPosterFetching() {
		if (getPostersTask != null) {
			getPostersTask.cancel();
		}
	}

	private void getPostersForVisibleMovies() {
		if (DEBUG) {
			Log.d(TAG, "getPostersForVisibleMovies()");
		}
		/* if adapterChildCount == 0 do nothing */
		if (adapterChildCount == 0) {
			return;
		}

		int firstPos = listView.getFirstVisiblePosition();
		int lastPos = listView.getLastVisiblePosition();

		if (lastPos == -1) {
			lastPos = DEFAULT_VISIBLE_ROWS - 1;
		}

		if (fragmentType == MovieListType.SEARCH) {
			while (lastPos > adapterChildCount) {
				lastPos -= 2;
			}
			/* listView.getLastVisiblePosition() returns 0 for some reason so just get 4 posters */
			List<Movie> movies = new ArrayList<Movie>();
			selectMovies(firstPos, lastPos, movies);
			getPostersTask = dataMananger.provideMoviePosters(movies, fragmentType);

			return;
		}

		/* don't include listview footer */
		if (lastPos == adapterChildCount) {
			lastPos--;
		}

		int firstPosOnNewPage = adapterChildCount - DEFAULT_ROWS;
		if (DEBUG) {
			Log.d(TAG, "pos = " + firstPos + " lastPos = " + lastPos + " firstPosOnNewPage " + firstPosOnNewPage);
		}
		if (lastPos == 0) {
			if ((lastPos = DEFAULT_VISIBLE_ROWS - 1) < adapterChildCount) {
				/* listView.getLastVisiblePosition() returns 0 for some reason so just get 4 posters */
				List<Movie> movies = new ArrayList<Movie>();
				selectMovies(firstPos, lastPos, movies);
				getPostersTask = dataMananger.provideMoviePosters(movies, fragmentType);
			}
		} else {
			List<Movie> movies = new ArrayList<Movie>();
			selectMovies(firstPos, lastPos, movies);
			getPostersTask = dataMananger.provideMoviePosters(movies, fragmentType);
		}
	}

	private void selectMovies(int pos, final int lastPos, final List<Movie> movies) {
		if (DEBUG) {
			Log.d(TAG, "inside selectMovies pos = " + pos + " lastPos = " + lastPos);
		}
		movies.clear();
		while (pos <= lastPos) {
			Movie[] moviePair = movieAdapter.getItem(pos);
			/* only include those posters that are recycled */
			if (moviePair[0].isPosterRecycled()) {
				movies.add(moviePair[FIRST_IN_A_PAIR]);
				if (DEBUG) {
					Log.e(TAG, "selectMovies movie " + moviePair[0].getName() + " poster for position " + pos);
				}
			}
			if (moviePair[SECOND_IN_A_PAIR] != null && moviePair[SECOND_IN_A_PAIR].isPosterRecycled()) {
				movies.add(moviePair[SECOND_IN_A_PAIR]);
				if (DEBUG) {
					Log.e(TAG, "selectMovies movie " + moviePair[SECOND_IN_A_PAIR].getName() + " poster for position "
							+ pos);
				}
			}
			pos++;
		}
	}

	private boolean isEndOfList() {
		/* don't trigger the list fetching if already went for the list */
		if (wentFetching) {
			return false;
		}
		if (fragmentType == MovieListType.SEARCH) {
			return false;
		}
		int lastPos = listView.getLastVisiblePosition();
		if (DEBUG) {
			Log.d(TAG, "lastPos " + lastPos + " adapterChildCount " + adapterChildCount);
		}
		if (lastPos == adapterChildCount) {
			wentFetching = true;
			return true;
		}
		return false;
	}

	private void addMoviePairsToAdapter(final List<Movie> movies, final MovieAdapter movieAdapter) {
		for (int i = 0; i < movies.size(); i++) {
			Movie[] moviePair = new Movie[2];
			moviePair[0] = movies.get(i);
			i++;
			/* there can be an uneven number of movies */
			if (i < movies.size()) {
				moviePair[1] = movies.get(i);
				movieAdapter.add(moviePair);
			}
		}
	}

	private boolean needRecycle(final int firstVisiblePositionEnd, final int lastVisiblePositionEnd) {
		if (firstVisPosScrollStart == firstVisiblePositionEnd) {
			return false;
		}
		if (lastVisPosScrollStart == firstVisiblePositionEnd) {
			lastVisPosScrollStart--;
			return true;
		}
		if (firstVisPosScrollStart == lastVisiblePositionEnd) {
			firstVisPosScrollStart++;
			/* don't take into account the footer */
			if (lastVisPosScrollStart == adapterChildCount) {
				lastVisPosScrollStart--;
			}
			return true;
		}
		if (lastVisPosScrollStart < firstVisiblePositionEnd) {
			return true;
		}
		if (firstVisPosScrollStart > lastVisiblePositionEnd) {
			if (lastVisPosScrollStart == adapterChildCount) {
				lastVisPosScrollStart--;
			}
			return true;
		}
		return false;
	}

	private void showFailureDialog() {
		/* hide progress bar when all bitmaps are loaded */
		progressBar.setVisibility(View.INVISIBLE);
		if (fragmentType != MovieListType.SEARCH) {
			AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
			/* if getting movie list failed re-try or exit */
			builder.setMessage(R.string.want_retry).setCancelable(false)
					.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
						@Override
						public void onClick(final DialogInterface dialog, final int id) {
							getListTask = dataMananger.provideMovieList(fragmentType, searchQuery);
						}
					}).setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
						@Override
						public void onClick(final DialogInterface dialog, final int id) {
							getActivity().finish();
						}
					});
			AlertDialog alert = builder.create();
			alert.show();
		} else {
			AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
			/* if getting movie list failed re-try or exit */
			builder.setMessage(R.string.search_failed).setCancelable(false)
					.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
						@Override
						public void onClick(final DialogInterface dialog, final int id) {
							/* kill this fragment */
							((FragmentActivity) getActivity()).getSupportFragmentManager().popBackStack();
						}
					});
			AlertDialog alert = builder.create();
			alert.show();
		}
	}
}
